﻿using ExtensionsAPI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace BleQua.Extensions {
	public class ExtensionsManager : IExtensionsManager {
		private readonly Dictionary<ExtensionType, Type> extensionsAssemblyTypes;
		public ExtensionsManager(string RootDirectory) {
			extensionsAssemblyTypes = new Dictionary<ExtensionType, Type>( );
			extensionsAssemblyTypes.Add(ExtensionType.ExtensionBase, typeof(ExtensionsAPI.IExtensionBase));
			extensionsAssemblyTypes.Add(ExtensionType.NetworkExtension, typeof(ExtensionsAPI.INetworkExtension));
			extensionsAssemblyTypes.Add(ExtensionType.TemplateExtension, typeof(ExtensionsAPI.ITemplateExtension));

			this.RootDirectory = RootDirectory;

			this.LoadPlugins(AppDomain.CurrentDomain.BaseDirectory + this.RootDirectory);
		}

		private Dictionary<String, IExtension> extensions = new Dictionary<String, IExtension>( );

		private void LoadPlugin(string FileName) {
			//Create a new assembly from the plugin file we're adding..
			System.Reflection.Assembly extensionAssembly = System.Reflection.Assembly.LoadFrom(FileName);
			System.IO.FileInfo extensionFile = new FileInfo(FileName);

			//Next we'll loop through all the Types found in the assembly
			foreach (Type extensionType in extensionAssembly.GetTypes( )) {
				//Only look at public types && Only look at non-abstract types
				Type interfaceType = extensionType.GetInterface(this.extensionsAssemblyTypes[ExtensionType.ExtensionBase].Name, true);
				if (extensionType.IsPublic && extensionType.IsClass && !extensionType.IsAbstract && interfaceType != null) {
					foreach (var p in extensionsAssemblyTypes) {
						//Gets a type object of the interface we need the plugins to match
						interfaceType = extensionType.GetInterface(p.Value.Name, true);

						//Make sure the interface we want to use actually exists
						if (interfaceType != null && interfaceType.FullName == p.Value.FullName) {
							//Create a new available plugin since the type implements the IPlugin interface
							IExtension newExtension = new Extension(FileName, extensionType.FullName);

							//Add the new plugin to our collection here
							this.extensions[extensionFile.Name.RemoveLast(4)] = newExtension;

							//var test = newExtension.CreateInstace<IExtensionBase>( );
							//cleanup a bit
							//newPlugin = null;
						}
					}
					//typeInterface = null; //Mr. Clean			
				}
			}
			//extensionAssembly = null; //more cleanup
		}


		/// <summary>
		/// Searches the passed Path for Plugins
		/// </summary>
		/// <param name="Path">Directory to search for Plugins in</param>
		private void LoadPlugins(string Path) {
			//First empty the collection, we're reloading them all
			UnloadPlugins( );

			//Go through all the files in the plugin directory
			if (Directory.Exists(Path)) {
				foreach (string fileOn in System.IO.Directory.GetFiles(Path)) {
					System.IO.FileInfo file = new System.IO.FileInfo(fileOn);

					//Preliminary check, must be .dll
					if (file.Extension.Equals(".dll")) {
						//Add the 'plugin'
						this.LoadPlugin(fileOn);
					}
				}
			}
		}


		private void UnloadPlugin(string Name) {
			if (extensions.ContainsKey(Name)) {
				extensions[Name].Dispose( );
				extensions.Remove(Name);
			}
		}

		private void UnloadPlugins( ) {
			foreach (var pluginOn in extensions) {
				//After we give the plugin a chance to tidy up, get rid of it
				pluginOn.Value.Dispose( );
			}

			//Finally, clear our collection of available plugins
			extensions.Clear( );
		}

		public void Dispose( ) {
			this.UnloadPlugins( );
		}

		/// <summary>
		/// Unloads and Closes all AvailablePlugins
		/// </summary>


		public string RootDirectory { get; private set; }

		public void InstallPlugin(string FileName) {
			throw new NotImplementedException( );
		}

		public void UninstallPlugin(string Name) {
			throw new NotImplementedException( );
		}

		public IEnumerable<IExtension> GetAllNetworkExtensions( ) {
			throw new NotImplementedException( );
		}

		public IEnumerable<IExtension> GetAllTemplateExtensions( ) {
			throw new NotImplementedException( );
		}

		public IEnumerable<IExtension> GetAllToolsExtensions( ) {
			throw new NotImplementedException( );
		}

		public IEnumerable<IExtension> GetAllExtensions( ) {
			throw new NotImplementedException( );
		}

		public IExtension Get(string Key) {
			throw new NotImplementedException( );
		}
	}
}
